/**
 * FIXME: 更新时会把正在执行的自定义任务也给停止了
 */
import { format as urlFormat } from 'url';
import { pick } from 'ramda';
import set from 'lodash/set';
import permissionCheckResolveDecorate from '@kuiper/auth/dist/server/lib/permissionCheckResolveDecorate';

import { JOB_TASK_TYPES, TASK_KEY_LABEL_MAP } from '../../../common/constants';
import { ensureWithBalance } from '../../plugins/billing';
import errorCodesBuilder from '../../helpers/errorCodesBuilder';
import fillAviliableFieldDefaultValue from './fillAviliableFieldDefaultValue';
import dataValidate from './dataValidate';

const errorCodes = errorCodesBuilder('更新任务失败');

export async function update({
	record,
	prevRecord,
	context,
	immediateExec = false,
}) {
	const {
		request: {
			log,
			headers: { cookie },
			auth: {
				credentials: { permissionMap },
			},
			server: {
				methods: { balanceTaskCountUpdate, createOrUpdateTask },
				app: {
					model: { Job, Balance },
				},
			},
		},
	} = context;

	// 套餐检查
	let balance;
	try {
		balance = await Balance.findOne({ uid: prevRecord.uid });
		if (!balance)
			throw new Error(`update job not found balance: uid(${prevRecord.uid})`);
	} catch (err) {
		const error = errorCodes['[03]编辑资产：查询关联balance记录失败'].from(err);
		log.error({ error }, error.rawMessage);
		throw error;
	}
	record = pick(
		JOB_TASK_TYPES.reduce(
			(arr, type) => {
				arr.push(`${type}Settings`);
				return arr;
			},
			[
				'sourceIp',
				'note',
				'enableMonitor',
				'enableAlert',
				'alertSettings',
				'prelogin',
				'customMonitorPage',
			]
		),
		record
	);
	record = dataValidate({ balance, record });
	record.enableMonitor = immediateExec || record.enableMonitor;

	if (
		!prevRecord.verification.isVerified &&
		!permissionMap['cloud-monitor\x01editJob\x010']
	) {
		const error = errorCodes['[03]不允许更新未通过验证的资产'];
		log.error({ error, target: prevRecord.target.host }, error.rawMessage);
		throw error;
	}

	if (
		record.vulSettings.enableSitePortraitTriggerMonitor &&
		!prevRecord.cydEnabled &&
		(record.enableMonitor || record.enableMonitor === prevRecord.enableMonitor)
	) {
		const error =
			errorCodes[
				// eslint-disable-next-line prettier/prettier
				'[03]网站画像：当前资产未接入知道创宇云防御，无法使用网站画像功能'
			];
		log.error({ error, target: prevRecord.targetUrl }, error.rawMessage);
		throw error;
	}

	if (record.enableMonitor) {
		fillAviliableFieldDefaultValue({ record });
	} else if (prevRecord.enableMonitor) {
		fillAviliableFieldDefaultValue({ record, prevRecord });
	}

	const resp = await Promise.allSettled(
		JOB_TASK_TYPES.map(taskType =>
			createOrUpdateTask({
				taskType,
				cookie,
				triggerType: 'schedule',
				body: {
					immediateExec,
					jobId: prevRecord._id,
					sourceIp: record.sourceIp || prevRecord.sourceIp,
					enableMonitor:
						record.enableMonitor === undefined
							? prevRecord.enableMonitor
							: record.enableMonitor,
					[`${taskType}Settings`]:
						record[`${taskType}Settings`] || prevRecord[`${taskType}Settings`],
					prelogin: record.prelogin,
					customMonitorPage: record.customMonitorPage,
				},
			})
		)
	);
	resp.forEach(x => {
		if (x.status === 'fulfilled') return;
		const error = errorCodes['[03]更新任务配置失败']
			.from(x.reason)
			.msg(x.reason.message || '');
		log.error({ error, record, reason: x.reason }, error.detail);
		throw error;
	});

	const enableTasks = [];
	const disabledTasks = [];

	for (let i = 0; i < JOB_TASK_TYPES.length; i++) {
		const taskType = JOB_TASK_TYPES[i];
		if ([].concat(record[`${taskType}Settings`]).some(x => x.enable)) {
			enableTasks.push(TASK_KEY_LABEL_MAP[taskType]);
		} else {
			disabledTasks.push(TASK_KEY_LABEL_MAP[taskType]);
		}
	}
	log.audit(
		{
			affects: [prevRecord.target.host],
			jobId: prevRecord.id,
			module: 'job',
			actionType: 'update',
			actionMessage: '更新监测配置',
			detail: `更新${record.note}资产配置，资产地址：${urlFormat(
				prevRecord.target
			)}，${record.enableMonitor ? '开启' : '关闭'}监测开关，${
				record.enableAlert ? '开启' : '关闭'
			}告警开关，${
				enableTasks.length > 0
					? `已启用任务：${enableTasks.join('、')}${
							disabledTasks.length > 0 ? '，' : ''
					  }`
					: ''
			}${
				disabledTasks.length > 0
					? `未启用任务：${disabledTasks.join('、')}。`
					: ''
			}`,
		},
		`[${prevRecord.target.host}]启用监测任务`
	);

	/**
	 * TODO: 如果修改了 sourceIp 需要重新验证域名所属权
	 */
	try {
		await Job.updateOne(
			{ _id: prevRecord._id },
			{
				$set: pick(
					[
						'sourceIp',
						'note',
						'enableMonitor',
						'enableAlert',
						'alertSettings',
						'httpAlertState',
					],
					record
				),
			}
		);
	} catch (err) {
		const error = errorCodes['[03]syncTask: 更新job记录'].from(err);
		log.error({ error, record: prevRecord }, error.rawMessage);
		throw error;
	}

	setTimeout(() => balanceTaskCountUpdate(prevRecord.uid), 1000);
}

async function resolve(root, args, context) {
	const prevRecord = context.prevRecord;
	const updateRecord = { ...prevRecord, ...args.record };

	return update({
		context,
		prevRecord,
		record: updateRecord,
		immediateExec: args.immediateExec,
	}).then(() => ({
		recordId: prevRecord._id,
		record: updateRecord,
	}));
}

const resolver = permissionCheckResolveDecorate({
	required: 'cloud-monitor\x01editJob',
	selfOnlyArgsBuilder(
		_source,
		args,
		{
			request: {
				auth: {
					credentials: { uid },
				},
			},
		}
	) {
		set(args, ['filter', 'uid'], uid);
	},
})(
	ensureWithBalance(async (root, args, context) => {
		const {
			request: {
				log,
				server: {
					app: {
						model: { Job },
					},
				},
			},
		} = context;

		let prevRecord;
		try {
			prevRecord = await Job.findOne(args.filter);
			if (!prevRecord) {
				throw new Error(
					`update job: mongo record not found ${JSON.stringify(args.record)}`
				);
			}
		} catch (err) {
			const error = errorCodes['[03]syncTask: 查询job记录'].from(err);
			log.error({ error, record: prevRecord }, error.rawMessage);
			throw error;
		}
		context.prevRecord = prevRecord.toObject();

		if (
			!prevRecord.verification.isVerified ||
			(!prevRecord.enableMonitor && !args.record.enableMonitor)
		) {
			return {
				server: context.request.server,
				uid: prevRecord.uid,
				ops: [],
			};
		}

		let enableSitePortraitTriggerMonitor =
			args.record?.vulSettings?.enableSitePortraitTriggerMonitor;
		if (enableSitePortraitTriggerMonitor === undefined) {
			enableSitePortraitTriggerMonitor =
				prevRecord.vulSettings.enable &&
				prevRecord.vulSettings.enableSitePortraitTriggerMonitor;
		}

		const ops = [];
		if (args.record.httpSettings !== undefined) {
			ops.push(
				{
					key: 'httpMaxTask',
					value: prevRecord.httpSettings.length,
				},
				{
					key: 'httpMaxTask',
					value: -1 * args.record.httpSettings.length,
				}
			);
		}
		if (args.record.pingSettings !== undefined) {
			ops.push(
				{
					key: 'pingMaxTask',
					value: prevRecord.pingSettings.length,
				},
				{
					key: 'pingMaxTask',
					value: -1 * args.record.pingSettings.length,
				}
			);
		}
		if (!prevRecord.enableMonitor && args.record.enableMonitor) {
			ops.push(
				{
					key: 'domainEnable',
					value: -1,
				},
				{
					key: 'sitePortrait',
					value: enableSitePortraitTriggerMonitor === false ? 0 : -1,
				}
			);
		}

		return {
			ops,
			server: context.request.server,
			uid: prevRecord.uid,
		};
	})(resolve)
);

export default {
	description: '更新资产',
	type: 'UpdateOneJobPayload',
	args: {
		immediateExec: 'Boolean',
		record: 'UpdateOneJobInput!',
		filter: 'FilterUpdateOneJobInput!',
	},
	resolve: resolver,
};
